/******************************************************************************
;	_tFIFO.c - FIFO code
;	Copyright (c) 2011 Matthew Villarrubia, Yves Santos. All rights reserved.
;	Any use of this software without prior written notice is prohibitted.
******************************************************************************/
//*****************************************************************************
//! \TODO Optimize for efficiency. Add FIFO_Put() and FIFO_Get() functions to
//! support adding multiple elements at once.
//*****************************************************************************
#include "FIFO.h"

//*****************************************************************************
//	Creates a circular FIFO. FIFOs are not thread safe. Use semaphores or
//	critical sections for thread safety.
//! \param fifo_capacity This is the maximum number of elements that can be
//! stored in this FIFO.
//! \param size This is the size (in bytes) of a single element in the FIFO.
//!	For portability reasons, use sizeof() for this paramater. Ex: To create a
//!	FIFO of 10 ints: FIFO_CreateFIFO(10,sizeof(int));
//! \return On success, returns a pointer to a FIFO. NULL otherwise.
//*****************************************************************************
_tFIFO
FIFO_CreateFifo(unsigned int fifo_capacity, size_t size){
	_tFIFO fifo;

	if(fifo_capacity < 1)
		return NULL;
	
	fifo = (_tFIFO)malloc(sizeof(_FIFO));
	
	if(fifo == NULL)
		return NULL;
	
	fifo->startDataPtr = malloc( (fifo_capacity + 1) * size * sizeof(char));

	if(fifo->startDataPtr == NULL){
		free(fifo);
		free(fifo->startDataPtr);
		return NULL;
	}
	fifo->endDataPtr = fifo->startDataPtr + (fifo_capacity * size);
	fifo->getPtr = fifo->startDataPtr;
	fifo->putPtr = fifo->startDataPtr + size;
	fifo->elementSize = size;
	return fifo;
}

static unsigned char*
next_ptr_pos(unsigned char* ptr, _tFIFO fifo){
	unsigned char* retPtr = ptr + (fifo)->elementSize;
	if( ptr > (fifo)->endDataPtr )
		retPtr = (fifo)->startDataPtr;
	return retPtr;
}

//*****************************************************************************
//	Puts a single element into the FIFO.
//! \param fifo Pointer to the FIFO to store the data in
//! \param data Data to be stored in the FIFO
//!	\return	Returns 0 on success. Returns -1 otherwise
//*****************************************************************************
int
FIFO_PutSingle(_tFIFO fifo, void* data){
	if(next_ptr_pos((fifo)->putPtr, fifo) == (fifo)->getPtr){
		return -1;
	}
	memcpy((fifo)->putPtr,data,(fifo)->elementSize);	
	(fifo)->putPtr = next_ptr_pos((fifo)->putPtr, fifo);

	return 0;
}

//*****************************************************************************
//	Pulls data off a FIFO.
//! \param fifo Pointer to the FIFO to retrieve the data from
//! \param data Pointer to data destination
//!	\return	Returns 0 on success. Returns -1 otherwise
//*****************************************************************************
int
FIFO_GetSingle(_tFIFO fifo, void* data){
	if(next_ptr_pos((fifo)->getPtr, fifo) == (fifo)->putPtr){
		return -1;
	}	
	(fifo)->getPtr = next_ptr_pos((fifo)->getPtr, fifo);
	memcpy(data,(fifo)->getPtr,(fifo)->elementSize);

	return 0;
}

//*****************************************************************************
//! \param fifo Pointer to the FIFO to retrieve the data from
//!	\return	Returns number of available spaces in the FIFO
//*****************************************************************************
unsigned int
FIFO_GetNumAvailableSpaces(_tFIFO fifo){
	unsigned int availableSpace;
	if((fifo)->getPtr > (fifo)->putPtr){
		availableSpace = (fifo)->getPtr - (fifo)->putPtr - (fifo)->elementSize;
	}else{
		availableSpace = (	(fifo)->endDataPtr - (fifo)->putPtr
			+((fifo)->getPtr - (fifo)->startDataPtr) + (fifo)->elementSize);
	}
	availableSpace = availableSpace / (fifo)->elementSize;

	return availableSpace;
}


//*****************************************************************************
//! \param fifo Pointer to the FIFO to retrieve the data from
//!	\return	Returns the number of elements stored in the FIFO
//*****************************************************************************
unsigned int
FIFO_GetSize(_tFIFO fifo){
	unsigned int size;
	size = (((fifo)->endDataPtr - (fifo)->startDataPtr) / (fifo)->elementSize)
		- FIFO_GetNumAvailableSpaces(fifo);
	return size;
}

int
FIFO_DeleteLastEntry(_tFIFO fifo){
	if(FIFO_GetSize(fifo) == 0)
		return -1;
	
	(fifo)->putPtr = (fifo)->putPtr - (fifo)->elementSize;

	if((fifo)->putPtr < (fifo)->startDataPtr)
		(fifo)->putPtr = (fifo)->endDataPtr;

	return 0;
}
	

//*****************************************************************************
//	Frees the memory associated with the provided FIFO
//! \param fifo Pointer to the FIFO that will be deleted
//!	\return	None
//*****************************************************************************
void
FIFO_Free(_tFIFO fifo){
	free((fifo)->startDataPtr);
	free(fifo);
}
